2 * Adium is the legal property of its developers, whose names are listed in the copyright file included
3 * with this source distribution.
5 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
6 * General Public License as published by the Free Software Foundation; either version 2 of the License,
7 * or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
10 * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
11 * Public License for more details.
13 * You should have received a copy of the GNU General Public License along with this program; if not,
14 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 #import <Adium/AIContactControllerProtocol.h>
18 #import <Adium/AIListGroup.h>
19 #import <Adium/AISortController.h>
20 #import <AIUtilities/AIArrayAdditions.h>
22 @interface AIListGroup (PRIVATE)
23 - (void)_recomputeVisibleCount;
26 @implementation AIListGroup
29 - (id)initWithUID:(NSString *)inUID
31 if ((self = [super initWithUID:inUID service:nil])) {
32 containedObjects = [[NSMutableArray alloc] init];
48 [containedObjects release]; containedObjects = nil;
53 /* An object ID generated by Adium that is shared by all objects which are, to most intents and purposes, identical to
54 * this object. Ths ID is composed of the service ID and UID, so any object with identical services and object ID's
55 * will have the same value here.
57 - (NSString *)internalObjectID
59 if (!internalObjectID) {
60 internalObjectID = [[AIListObject internalObjectIDForServiceID:@"Group" UID:[self UID]] retain];
62 return internalObjectID;
66 * @brief Generate a special identifier for this group based upon its contents
68 * This is useful for storing preferences which are related not to the name of this group (which might be arbitrary) but
69 * rather to its contents. The contact list root always returns its own UID, but other groups will have a different
70 * contentsBasedIdentifier depending upon what other objects they contain.
72 - (NSString *)contentsBasedIdentifier
74 NSString *contentsBasedIdentifier;
75 if (self == [[adium contactController] contactList]) {
76 contentsBasedIdentifier = [self UID];
79 NSArray *UIDArray = [[containedObjects valueForKey:@"UID"] sortedArrayUsingSelector:@selector(compare:)];
80 contentsBasedIdentifier = [UIDArray componentsJoinedByString:@";"];
81 if (![contentsBasedIdentifier length]) contentsBasedIdentifier = [self UID];
84 return contentsBasedIdentifier;
87 //Visibility -----------------------------------------------------------------------------------------------------------
88 #pragma mark Visibility
90 The visible objects contained in a group are always sorted to the top. This allows us to easily retrieve only visible
91 objects without having to physically remove invisible objects from the group.
93 //Returns the number of visible objects in this group
94 - (unsigned)visibleCount
99 //Cache the number of contained objects that are visible
100 - (void)_recomputeVisibleCount
104 NSEnumerator *containedObjectEnumerator = [[self containedObjects] objectEnumerator];
105 AIListObject *containedObject = nil;
107 while ((containedObject = [containedObjectEnumerator nextObject])){
108 if ([containedObject visible]){
110 } else if ([containedObject isKindOfClass:[AIListGroup class]] &&
111 ([[adium contactController] isGroupDetached:self] || [containedObject alwaysVisible])) {
116 [self setStatusObject:(visibleCount ? [NSNumber numberWithInt:visibleCount] : nil)
117 forKey:@"VisibleObjectCount"
121 //Called when the visibility of an object in this group changes
122 - (void)visibilityOfContainedObject:(AIListObject *)inObject changedTo:(BOOL)inVisible
124 //Update our visibility as a result of this change
125 [self _recomputeVisibleCount];
127 //Sort the contained object to or from the bottom (invisible section) of the group
128 [[adium contactController] sortListObject:inObject];
131 //Object Storage ---------------------------------------------------------------------------------------------
132 #pragma mark Object Storage
133 //Return our contained objects
134 - (NSArray *)containedObjects
136 return containedObjects;
139 //Number of containd objects
140 - (unsigned)containedObjectsCount
142 return [containedObjects count];
145 //Test for the presence of an object in our group
146 - (BOOL)containsObject:(AIListObject *)inObject
148 return [containedObjects containsObject:inObject];
151 - (BOOL)canContainOtherContacts {
155 - (BOOL)containsMultipleContacts {
159 //Retrieve an object by index
160 - (id)objectAtIndex:(unsigned)index
162 return [containedObjects objectAtIndex:index];
165 //Retrieve the index of an object
166 - (int)indexOfObject:(AIListObject *)inObject
168 return [containedObjects indexOfObject:inObject];
171 - (NSArray *)listContacts
173 return containedObjects;
176 //Remove all the objects from this group (PRIVATE: For contact controller only)
177 - (void)removeAllObjects
179 //Remove all the objects
180 while ([containedObjects count]) {
181 [self removeObject:[containedObjects objectAtIndex:0]];
185 //Retrieve a specific object by service and UID
186 - (AIListObject *)objectWithService:(AIService *)inService UID:(NSString *)inUID
188 NSEnumerator *enumerator = [containedObjects objectEnumerator];
189 AIListObject *object;
191 while ((object = [enumerator nextObject])) {
192 if ([inUID isEqualToString:[object UID]] && [object service] == inService) break;
199 * @brief Add an object to this group
201 * PRIVATE: For contact controller only. Sorting and visible count updating will be performed as needed.
203 * @result YES if the object was added (that is, was not already present)
205 - (BOOL)addObject:(AIListObject *)inObject
209 if (![containedObjects containsObjectIdenticalTo:inObject]) {
211 [inObject setContainingObject:self];
212 [containedObjects addObject:inObject];
214 //Update our visible count
215 [self _recomputeVisibleCount];
217 /* Sort this object on our own. This always comes along with a content change, so calling contact controller's
218 * sort code would invoke an extra update that we don't need. We can skip sorting if this object is not visible,
219 * since it will add to the bottom/non-visible section of our array.
221 if ([inObject visible]) {
222 [self sortListObject:inObject
223 sortController:[[adium contactController] activeSortController]];
227 [self setStatusObject:[NSNumber numberWithInt:[containedObjects count]]
228 forKey:@"ObjectCount"
237 //Remove an object from this group (PRIVATE: For contact controller only)
238 - (void)removeObject:(AIListObject *)inObject
240 if ([containedObjects containsObject:inObject]) {
242 [inObject setContainingObject:nil];
243 [containedObjects removeObject:inObject];
245 //Update our visible count
247 [self _recomputeVisibleCount];
250 [self setStatusObject:[NSNumber numberWithInt:[containedObjects count]]
251 forKey:@"ObjectCount"
256 //Move group from one contact list to another
257 - (BOOL)moveGroupTo:(AIListObject<AIContainingObject> *)list
259 return [self moveGroupFrom:[self containingObject] to:list];
262 - (BOOL)moveGroupFrom:(AIListObject<AIContainingObject> *)fromList to:(AIListObject<AIContainingObject> *)toList
264 // Check if group is not already there
265 if([toList containsObject:self])
268 [fromList removeObject:self];
269 [toList addObject:self];
270 [self setContainingObject:toList];
275 - (BOOL)moveAllGroupsFrom:(AIListGroup *)fromContactList to:(AIListGroup *)toContactList {
276 NSEnumerator *groups = [containedObjects objectEnumerator];
279 while ((group = [groups nextObject]))
280 [group moveGroupTo:toContactList];
285 //Sorting --------------------------------------------------------------------------------------------------------------
287 //Resort an object in this group (PRIVATE: For contact controller only)
288 - (void)sortListObject:(AIListObject *)inObject sortController:(AISortController *)sortController
291 [containedObjects removeObject:inObject];
292 [containedObjects insertObject:inObject
293 atIndex:[sortController indexForInserting:inObject
294 intoObjects:containedObjects]];
298 //Resorts the group contents (PRIVATE: For contact controller only)
299 - (void)sortGroupAndSubGroups:(BOOL)subGroups sortController:(AISortController *)sortController
301 //Sort the groups within this group
303 NSEnumerator *enumerator;
304 AIListObject *object;
306 enumerator = [containedObjects objectEnumerator];
307 while ((object = [enumerator nextObject])) {
308 if ([object isMemberOfClass:[AIListGroup class]]) {
309 [(AIListGroup *)object sortGroupAndSubGroups:YES
310 sortController:sortController];
316 if (sortController) {
317 NSMutableArray *sortedListObjects;
319 if ([containedObjects count] > 1) {
320 sortedListObjects = [[sortController sortListObjects:containedObjects] mutableCopy];
321 [containedObjects release]; containedObjects = sortedListObjects;
327 //Expanded State -------------------------------------------------------------------------------------------------------
328 #pragma mark Expanded State
329 //Set the expanded/collapsed state of this group (PRIVATE: For the contact list view to let us know our state)
330 - (void)setExpanded:(BOOL)inExpanded
332 expanded = inExpanded;
333 loadedExpanded = YES;
335 //Returns the current expanded/collapsed state of this group
338 if (!loadedExpanded) {
339 loadedExpanded = YES;
340 expanded = [[self preferenceForKey:@"IsExpanded"
341 group:@"Contact List"] boolValue];
353 - (void)listObject:(AIListObject *)listObject didSetOrderIndex:(float)inOrderIndex
355 if (inOrderIndex > largestOrder) {
356 largestOrder = inOrderIndex;
357 } else if (inOrderIndex < smallestOrder) {
358 smallestOrder = inOrderIndex;
362 - (float)smallestOrder
364 return smallestOrder;
367 - (float)largestOrder
372 #pragma mark Applescript
373 - (NSScriptObjectSpecifier *)objectSpecifier
375 NSScriptClassDescription *containerClassDesc = (NSScriptClassDescription *)[NSScriptClassDescription classDescriptionForClass:[NSApp class]];
376 return [[[NSNameSpecifier alloc]
377 initWithContainerClassDescription:containerClassDesc
378 containerSpecifier:nil key:@"contactGroups"
379 name:[self UID]] autorelease];
382 - (NSArray *)contacts
384 return [self containedObjects];